"""
文件处理工具
"""

import os
import shutil
from pathlib import Path
from typing import List, Optional, Union
from app.core.logging import get_logger
from app.core.exceptions import FileProcessingError

logger = get_logger(__name__)


def ensure_directory(directory: Union[str, Path]) -> Path:
    """
    确保目录存在，如果不存在则创建
    
    Args:
        directory: 目录路径
        
    Returns:
        目录路径对象
        
    Raises:
        FileProcessingError: 创建目录失败时抛出
    """
    try:
        path = Path(directory)
        path.mkdir(parents=True, exist_ok=True)
        return path
    except Exception as e:
        raise FileProcessingError(f"创建目录失败: {directory}", details={"error": str(e)})


def get_file_extension(filename: str) -> str:
    """
    获取文件扩展名
    
    Args:
        filename: 文件名
        
    Returns:
        文件扩展名（包含点号）
    """
    return Path(filename).suffix.lower()


def is_allowed_file(filename: str, allowed_extensions: List[str]) -> bool:
    """
    检查文件是否为允许的类型
    
    Args:
        filename: 文件名
        allowed_extensions: 允许的扩展名列表
        
    Returns:
        是否为允许的文件类型
    """
    if not filename or not allowed_extensions:
        return False
    
    extension = get_file_extension(filename)
    return extension in [ext.lower() for ext in allowed_extensions]


def get_file_size(filepath: Union[str, Path]) -> int:
    """
    获取文件大小（字节）
    
    Args:
        filepath: 文件路径
        
    Returns:
        文件大小
        
    Raises:
        FileProcessingError: 获取文件大小失败时抛出
    """
    try:
        return Path(filepath).stat().st_size
    except Exception as e:
        raise FileProcessingError(f"获取文件大小失败: {filepath}", details={"error": str(e)})


def safe_filename(filename: str) -> str:
    """
    生成安全的文件名（移除危险字符）
    
    Args:
        filename: 原始文件名
        
    Returns:
        安全的文件名
    """
    if not filename:
        return "unnamed_file"
    
    # 移除路径分隔符和其他危险字符
    import re
    safe_name = re.sub(r'[<>:"/\\|?*]', '_', filename)
    
    # 移除开头和结尾的点号和空格
    safe_name = safe_name.strip('. ')
    
    # 确保文件名不为空
    if not safe_name:
        return "unnamed_file"
    
    # 限制文件名长度
    if len(safe_name) > 255:
        name_part = Path(safe_name).stem[:200]
        ext_part = Path(safe_name).suffix
        safe_name = name_part + ext_part
    
    return safe_name


def copy_file(src: Union[str, Path], dst: Union[str, Path]) -> Path:
    """
    复制文件
    
    Args:
        src: 源文件路径
        dst: 目标文件路径
        
    Returns:
        目标文件路径
        
    Raises:
        FileProcessingError: 复制失败时抛出
    """
    try:
        src_path = Path(src)
        dst_path = Path(dst)
        
        # 确保目标目录存在
        ensure_directory(dst_path.parent)
        
        # 复制文件
        shutil.copy2(src_path, dst_path)
        
        logger.info(f"文件复制成功: {src} -> {dst}")
        return dst_path
        
    except Exception as e:
        raise FileProcessingError(f"复制文件失败: {src} -> {dst}", details={"error": str(e)})


def move_file(src: Union[str, Path], dst: Union[str, Path]) -> Path:
    """
    移动文件
    
    Args:
        src: 源文件路径
        dst: 目标文件路径
        
    Returns:
        目标文件路径
        
    Raises:
        FileProcessingError: 移动失败时抛出
    """
    try:
        src_path = Path(src)
        dst_path = Path(dst)
        
        # 确保目标目录存在
        ensure_directory(dst_path.parent)
        
        # 移动文件
        shutil.move(str(src_path), str(dst_path))
        
        logger.info(f"文件移动成功: {src} -> {dst}")
        return dst_path
        
    except Exception as e:
        raise FileProcessingError(f"移动文件失败: {src} -> {dst}", details={"error": str(e)})


def delete_file(filepath: Union[str, Path]) -> bool:
    """
    删除文件
    
    Args:
        filepath: 文件路径
        
    Returns:
        是否删除成功
    """
    try:
        path = Path(filepath)
        if path.exists() and path.is_file():
            path.unlink()
            logger.info(f"文件删除成功: {filepath}")
            return True
        return False
    except Exception as e:
        logger.error(f"删除文件失败: {filepath}, 错误: {e}")
        return False


def cleanup_directory(directory: Union[str, Path], max_age_days: int = 7) -> int:
    """
    清理目录中的旧文件
    
    Args:
        directory: 目录路径
        max_age_days: 文件最大保留天数
        
    Returns:
        删除的文件数量
    """
    try:
        from datetime import datetime, timedelta
        
        path = Path(directory)
        if not path.exists() or not path.is_dir():
            return 0
        
        cutoff_time = datetime.now() - timedelta(days=max_age_days)
        deleted_count = 0
        
        for file_path in path.iterdir():
            if file_path.is_file():
                file_time = datetime.fromtimestamp(file_path.stat().st_mtime)
                if file_time < cutoff_time:
                    try:
                        file_path.unlink()
                        deleted_count += 1
                        logger.debug(f"删除旧文件: {file_path}")
                    except Exception as e:
                        logger.warning(f"删除文件失败: {file_path}, 错误: {e}")
        
        logger.info(f"目录清理完成: {directory}, 删除了 {deleted_count} 个文件")
        return deleted_count
        
    except Exception as e:
        logger.error(f"清理目录失败: {directory}, 错误: {e}")
        return 0


def read_text_file(filepath: Union[str, Path], encoding: str = 'utf-8') -> str:
    """
    读取文本文件内容
    
    Args:
        filepath: 文件路径
        encoding: 文件编码
        
    Returns:
        文件内容
        
    Raises:
        FileProcessingError: 读取失败时抛出
    """
    try:
        with open(filepath, 'r', encoding=encoding) as f:
            return f.read()
    except Exception as e:
        raise FileProcessingError(f"读取文件失败: {filepath}", details={"error": str(e)})


def write_text_file(filepath: Union[str, Path], content: str, encoding: str = 'utf-8') -> None:
    """
    写入文本文件
    
    Args:
        filepath: 文件路径
        content: 文件内容
        encoding: 文件编码
        
    Raises:
        FileProcessingError: 写入失败时抛出
    """
    try:
        path = Path(filepath)
        ensure_directory(path.parent)
        
        with open(path, 'w', encoding=encoding) as f:
            f.write(content)
            
        logger.info(f"文件写入成功: {filepath}")
        
    except Exception as e:
        raise FileProcessingError(f"写入文件失败: {filepath}", details={"error": str(e)})
